home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
GameKit
/
gamekit-1
/
GameBrain.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
16KB
|
504 lines
/* Generated by Interface Builder */
#import <gamekit/gamekit.h>
#import <daymisckit/daymisckit.h>
#import <stdio.h>
#import <string.h>
#import <objc/objc-runtime.h>
@implementation GameBrain
- init // designated initializer sets up game variables
{ // to sensible values.
[super init];
level = 0;
paused = NO;
ranOnce = NO;
aborting = NO;
printLevel = NO;
initDone = NO;
return self;
}
/* methods to get at important variables */
- highScoreController { return highScoreController; }
- oneUpView { return oneUpView; }
- scoreKeeper { return scoreKeeper; }
- scorePlayer { return scorePlayer; }
- soundPlayer { return soundPlayer; }
- mainStrings { return strings; }
- gameScreen { return gameScreen; }
- gameWindow { return gameWindow; }
- preferencesBrain { return preferencesBrain; }
- infoController { return infoController; }
- gameInfo { return gameInfo; }
- (BOOL)aborting { return aborting; }
- makeGameInfo { return [[GameInfo alloc] init]; }
- (int)startLevel { return [preferencesBrain startLevel]; }
- (int)tableNum { return tableNum; }
- (int)level { return level; }
- (int)speed { return [preferencesBrain speed]; }
- (int)paused { return paused; }
- (BOOL)playerCheated { return playerCheated; }
- gameOver:sender // end the game, take high scores, etc.
{ // calls gameOver method (this is IB wrapper)
[self gameOver];
return self;
}
- pauseGame:sender // toggle pause status of the game
{
const char *title = [pauseMenuCell title];
if (!strcmp(title,[strings valueForStringKey:"Pause"])) {
[self pause];
} else {
[self unpause];
}
return self;
}
- (int)pause // pause game
{
if ([gameScreen gameState]==GAMEOVER) return NO; // no pausing if over
[gameTimer pauseTiming:self];
#ifdef NOISYDEBUG
fprintf(stderr, "Pause timer, elapsed time is %s\n",
[gameTimer stringValue]);
#endif
[pauseMenuCell setTitle:[strings valueForStringKey:"Unpause"]];
[gameScreen pause:self];
if (!paused)
[gameWindow setTitle:[strings valueForStringKey:"PausedTitle"]];
paused = YES;
return YES;
}
- unpause // unpause game
{
if ([gameScreen gameState]==GAMEOVER) [pauseMenuCell setEnabled:NO];
[pauseMenuCell setTitle:[strings valueForStringKey:"Pause"]];
[gameScreen unpause:self];
[gameWindow setTitle:[strings valueForStringKey:"GameName"]];
paused = NO;
#ifdef NOISYDEBUG
fprintf(stderr, "Unpause game timer\n");
#endif
[gameTimer continueTiming:self];
return self;
}
- startNewGame:sender // starts a new game
{
if (![self askAbortGame:GK_ABORT]) return nil;
if ([gameScreen gameState] != GAMEOVER) {
aborting = YES;
[self gameOver];
}
[scoreKeeper resetScore];
[scoreKeeper updateTopScoreText];
[scoreKeeper updateScoreText];
level = 0;
playerCheated = NO;
ranOnce = YES;
[pauseMenuCell setEnabled:YES];
[self layerWindows];
level = [preferencesBrain startLevel] - 1; // so all level init is done
[preferencesBrain startingGame];
[oneUpView setNumUp:[gameInfo numOneUps]];
[self nextLevel]; // by the nextLevel method.
if (paused) [self unpause];
[gameWindow makeKeyAndOrderFront:self];
// use sender's tag to determine which HS table to use. (Expert, Beg., etc)
tableNum = 0; // should be the prefs default here *****
if ([sender respondsTo:@selector(tag)]) {
int theTag = [sender tag];
if (theTag > 0) tableNum = theTag;
}
//[highScoreController table:tableNum]; // get applicable high score table
currentSlot = [self buildNewSlot];
#ifdef NOISYDEBUG
fprintf(stderr, "Starting time is: %s\n",
[[currentSlot startTime] stringValue]);
#endif
[gameTimer startTiming:self];
[gameScreen restartGame];
#ifdef GK_USE_MUSICKIT
if ([preferencesBrain music] && ([gameScreen gameState] != GAMEOVER))
[scorePlayer play:self];
#endif
aborting = NO;
return self;
}
- unpauseGame:sender // unpause the game
{
return [self pauseGame:sender]; // handles toggle of menuCell
}
- nextLevel // move to next level-- called when level is
// completed and to start game
{
level++;
[levelText setIntValue:level];
[gameScreen setUpScreen];
return self;
}
- nextLevel:sender // this is can be called from the interface; it considers
// the user to have cheated since they haven't completed the level normally.
{
playerCheated = YES;
return [self nextLevel];
}
- buildNewSlot
{ // build a high score slot at the start of a game.
id newSlot;
if (gameInfo) { // use class in GameInfo...must be linked in already!
newSlot = [[objc_lookUpClass([[gameInfo slotType] stringValue])
alloc] init];
} else newSlot = [[HighScoreSlot alloc] init];
gameTimer = [[DAYStopwatch alloc] init];
[newSlot setElapsedTime:gameTimer];
[newSlot setStartTime:[[DAYTime alloc] initWithCurrentTime]];
[newSlot setPlayerName:[preferencesBrain defaultPlayerName]];
[newSlot setUserName:[NXApp userLoginName]];
[newSlot setMachineName:[NXApp realHostName]];
[newSlot setStartLevel:[preferencesBrain startLevel]];
[newSlot setEndLevel:[preferencesBrain startLevel]];
return newSlot;
}
- currentHighScoreSlot
{
// update the slot and return it.
[currentSlot setEndLevel:level];
if ([gameScreen gameState] != GAMEOVER)
[currentSlot setEndTime:[[DAYTime alloc] initWithCurrentTime]];
[currentSlot setFinalScore:[scoreKeeper currentScore]];
[gameTimer calcElapsedTime:self];
#ifdef NOISYDEBUG
fprintf(stderr, "Current elapsed time is: %s\n", [gameTimer stringValue]);
#endif
return currentSlot;
}
- gameOver // tidy up game, allow high score name entry
{
[gameTimer pauseTiming:self]; // stop the timer now that the game is over
// implies a -calcElapsedTime: message, so we don't send it from here
[currentSlot setEndTime:[[DAYTime alloc] initWithCurrentTime]];
#ifdef NOISYDEBUG
fprintf(stderr, "Ending elapsed time is: %s\n", [gameTimer stringValue]);
fprintf(stderr, "Ending time is: %s (%s elapsed)\n",
[[currentSlot endTime] stringValue],
[[currentSlot elapsedTime] stringValue]);
#endif
gameTimer = nil;
// remove demo mode title...
if ([gameScreen demoMode])
[gameWindow setTitle:[strings valueForStringKey:"GameName"]];
// if not demo, it's possible to get a high score entry...
else if ([gameScreen gameState] != GAMEOVER)
[highScoreController putInHighScores:[self currentHighScoreSlot]];
[pauseMenuCell setEnabled:NO];
#ifdef GK_USE_MUSICKIT
[scorePlayer stop:self];
#endif
currentSlot = nil;
[gameScreen gameOver]; // change gameView's state to GAMEOVER
if (paused) [self unpause];
return self;
}
- printGame:sender // print game screen w/score, level, etc.
{
char titleString[80];
if (printLevel) sprintf(titleString,
[strings valueForStringKey:"PrintTitleLevel"], level,
[scoreKeeper currentScore], [highScoreController highestScore]);
else sprintf(titleString,[strings valueForStringKey:"PrintTitle"],
[scoreKeeper currentScore], [highScoreController highestScore]);
[self pause]; // bringing up the print panel will do this anyway,
// indirectly, via the delegate methods, but that would screw up the
// window title bar we want to put up here: (score, highest score)
[gameWindow setTitle:titleString];
[gameWindow smartPrintPSCode:self];
if (paused)
[gameWindow setTitle:[strings valueForStringKey:"PausedTitle"]];
else [gameWindow setTitle:[strings valueForStringKey:"GameName"]];
return self;
}
// Application DELEGATE methods. Special things to do on startup, unhide,
// hide, and so on.
- buildAlert
{
if (!alert) alert = NXGetAlertPanel([strings valueForStringKey:"GameName"],
[strings valueForStringKey:"LoadingMessage"], NULL, NULL, NULL);
return self;
}
- appWillInit:sender // after init, but before 1st event.
{
[self buildAlert];
[alert makeKeyAndOrderFront:self];
[loadingText setStringValue:[strings valueForStringKey:"LoadInit"]];
NXPing();
return self;
}
- appDidInit:sender // after init, but before 1st event.
{
NXEvent *theEvent = (NXEvent *)malloc(sizeof(NXEvent));
if (!gameInfo) gameInfo = [self makeGameInfo];
[pauseMenuCell setEnabled:NO];
if (alert != loadingPanel) [alert orderOut:self];
// methods to load the stuff that takes a while.
[loadingPanel makeKeyAndOrderFront:self];
// automatically make a bunch of the needed connections if they
// haven't already been made in the .nib file and inform the
// main objects that the app is up and running.
if (!gameWindow) gameWindow = [gameScreen window];
if (![gameWindow delegate]) [gameWindow setDelegate:self];
[infoController appDidInit:sender];
[loadingText setStringValue:[strings valueForStringKey:"LoadPrefs"]];
NXPing();
[preferencesBrain appDidInit:sender];
[[levelText window] setFrameUsingName:"Stats"];
[[levelText window] setFrameAutosaveName:"Stats"];
[gameWindow setFrameUsingName:"Game"];
[gameWindow setFrameAutosaveName:"Game"];
[loadingText setStringValue:[strings valueForStringKey:"LoadSound"]];
NXPing();
[soundPlayer appDidInit:sender];
[loadingText setStringValue:[strings valueForStringKey:"LoadImages"]];
NXPing();
[gameScreen appDidInit:sender];
#ifdef GK_USE_MUSICKIT
[loadingText setStringValue:[strings valueForStringKey:"LoadScore"]];
NXPing();
[scorePlayer appDidInit:sender];
#endif
[loadingText setStringValue:[strings valueForStringKey:"LoadHighs"]];
NXPing();
[highScoreController appDidInit:sender];
[levelText setIntValue:1];
[scoreKeeper appDidInit:sender];
if (oneUpView) [scoreKeeper addDelegate:oneUpView];
[loadingPanel orderOut:self];
[[gameScreen animate:self] update]; // start up animation
if ((alert != loadingPanel) && alert) NXFreeAlertPanel(alert);
// the app-defined event will be the first one, used to bring up the
// various panels that may turn up during the start up sequence.
// (i.e. welcome, readme, shareware alert.)
theEvent->type = NX_APPDEFINED;
theEvent->data.compound.subtype = GK_STARTUP;
DPSPostEvent(theEvent, YES); // post as the next event to be serviced.
return self;
}
- startUp // handle the startup app-defined event
{
BOOL firstTimeRun = [preferencesBrain firstTimeCheck];
if ([infoController notRegistered]) { // shareware alert always...
NXRunAlertPanel(
[strings valueForStringKey:"SharewareAlert"],
[strings valueForStringKey:"SharewareMessage"],
[strings valueForStringKey:"Understand"], NULL, NULL);
}
if (!firstTimeRun && [preferencesBrain autoStart])
[self startNewGame:self];
[self layerWindows];
if (firstTimeRun) { // welcome and readme
char *str = malloc(128);
sprintf(str, [strings valueForStringKey:"Welcome1"],
[infoController versionString]);
NXRunAlertPanel([strings valueForStringKey:"Welcome"],
str, [strings valueForStringKey:"LetsPlay"],
NULL, NULL);
free(str);
[infoController readme:self];
}
initDone = YES;
return self;
}
- applicationDefined:(NXEvent *)theEvent // initial startup readme panels
{ // a subclass could add other event types, but don't forget to call super!
// You'd probably want to structure your code more or less like what
// you see here (switch), if you have several event types declared.
switch (theEvent->data.compound.subtype) { // switch allows me to
// easily add more event types if I need to do so.
case GK_STARTUP : { [self startUp]; break; }
default : { break; }
}
return self;
}
- appDidBecomeActive:sender
{ // why the "initDone" ivar?
// don't want to order the window up until we've got return values from
// the alert panels that turn up when the game starts. Since this message
// is sent after the alert comes up but before the user replies (this
// message is sent when the event loop becomes active, including launch)
// we need to NOT order the gamewindow, etc. out until the start up
// sequence is complete. This is a hack that removes an annoying bug
// that occured with panels popping up and making a general mess of the
// screen...since in 2.x this message was _NOT_ sent on the initial
// launch's activation of the event loop. The 3.x behavior is more
// correct, I suppose, but it was annoying to track down why this was
// happening. Anyway, this works around the problem just fine.
if (initDone) [self layerWindows];
return self;
}
- appDidHide:sender
{
[self pause];
return self; // pause game on Command-h
}
- appDidResignActive:sender
{
if ([gameScreen demoMode]) return self;
[self pause];
return self; // pause game on app deactivate
}
- layerWindows
{
[gameWindow makeKeyAndOrderFront:self];
[gameWindow makeFirstResponder:gameScreen];
if ([preferencesBrain autoUnPause]) [self unpause];
// make sure the windows are layered properly
// need to check delegate to be sure it responds...
// Note: NeXT has said, for portability, that we shouldn't depend on
// a method to return zero if sent to a nil object unless that method
// returns an id, in which case nil will be "returned". Thus, the
// first half of the condition below has been put in to deal with
// when there's no delegate. It's not really needed on Intel or Moto
// hardware, but I'm keeping later ports in mind here, too...
if (![[levelText window] delegate] || // if no delegate, assume window up
[[[levelText window] delegate] windowUp])
[[levelText window] orderFront:self];
[gameWindow orderFront:self];
return self;
}
- appDidUnhide:sender
{
return [self layerWindows];
}
- appWillTerminate:sender // update DEFAULTS here
{
[preferencesBrain writeDefaults:self];
return self;
}
- (BOOL)askAbortGame:(int)why
{
if (([gameScreen gameState] != GAMEOVER) && (![gameScreen demoMode]) &&
([preferencesBrain alert])) {
BOOL flag = (why == GK_EXIT);
// Verify that player wants to leave game
[self pause];
if (NXRunAlertPanel(([highScoreController
slotIsEligible:[self currentHighScoreSlot]] ?
[strings valueForStringKey:"HaveScore"] : NULL),
(flag ? [strings valueForStringKey:"ReallyQuit"] :
[strings valueForStringKey:"ThrowAway"]),
(flag ? [strings valueForStringKey:"Yes2"] :
[strings valueForStringKey:"Yes1"]),
(flag ? [strings valueForStringKey:"No2"] :
[strings valueForStringKey:"No1"]),
NULL) != NX_ALERTDEFAULT) {
[self unpause]; // unpause if we're aborting the game abort. :->
return NO;
} }
return YES;
}
- abortGame:sender
{
if ([self askAbortGame:GK_ABORT]) {
aborting = YES;
[self gameOver];
}
return self;
}
- quit:sender
{
if (![self askAbortGame:GK_EXIT]) return self;
if ([infoController notRegistered]) {
if (ranOnce) {
NXRunAlertPanel([strings valueForStringKey:"Goodbye"],
[strings valueForStringKey:"Enjoyed"],
[strings valueForStringKey:"NoForget"], NULL, NULL);
} else {
NXRunAlertPanel([strings valueForStringKey:"Goodbye"],
[strings valueForStringKey:"TryMe"],
[strings valueForStringKey:"TryLater"], NULL, NULL);
}
}
[highScoreController closeServers]; // make sure server knows that we left.
return [NXApp terminate:sender];
}
- windowDidResginMain:sender // do pause if window loses main status
{
[self pause];
return self; // pause game
}
- windowDidResignKey:sender // do pause if window loses key status
{
[self pause];
return self; // pause game
}
- windowDidBecomeKey:sender // do unpause if window gains key status
{
[gameWindow makeFirstResponder:gameScreen];
if ([preferencesBrain autoUnPause]) [self unpause]; // unpause on unhide*/
return self;
}
- windowDidDeminiaturize:sender // clean off crap left by the new
{ // border animation in 3.0. yuck!
[gameScreen update];
[[infoController niftyView] update];
return self;
}
- windowDidMove:sender // move status with game window
{
NXRect gameFrame, statsFrame;
[gameWindow getFrame:&gameFrame];
[[levelText window] getFrame:&statsFrame];
[[levelText window] moveTo:(NX_X(&gameFrame) - NX_WIDTH(&statsFrame) + 1)
:NX_Y(&gameFrame) + NX_HEIGHT(&gameFrame) - NX_HEIGHT(&statsFrame)];
return self;
}
@end